package breadbox.client;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import breadbox.client.logging.Log;
import breadbox.client.logging.Logger;
import breadbox.client.operations.Delete;
import breadbox.client.operations.Get;
import breadbox.client.operations.Put;
import breadbox.client.operations.OperationType;
import breadbox.client.operations.RMIMessageReceiver;
import breadbox.dataManagement.RMIMessage;
import breadbox.pastry.Message;
/**
 * Main class to handle Client execution
 * @author Forrest
 *
 */
public class Client {
	
	private static int chunkSize;
	private static boolean encrypt, compress, hash, publicfile;
	
	public static void main(String[] args) throws RemoteException {
		// assign values to options in case they aren't specified later
		chunkSize = -1;
		encrypt = false;
		compress = false;
		hash = false;
		publicfile = true;
		
		// exit early if no arguments are present
		if (args.length < 1) {
			System.err.println("Error: Too few arguments:");
			printUsage();
		}
		
		// parse configuration options
		int i = 0;
		for (i = 0; i < args.length; i++) {
			if (!args[i].startsWith("-")) {
				break;
			}
			if (args[i].equals("-chunksize")) {	// set chunkSize
				if (i < args.length - 1) {
					i++;
					chunkSize = Integer.valueOf(args[i]);
				} else {
					printUsage();
				}
			} else if (args[i].equals("-e")) {	// enable encryption
				encrypt = true;
			} else if (args[i].equals("-c")) {	// enable compression
				compress = true;
			} else if (args[i].equals("-h")) {	// enable hash checking
				hash = true;
			} else if (args[i].equals("-p")) {
				publicfile = false;
			}
		}

		// if there is no command argument, exit early
		if (i == args.length) {
			printUsage();
		}
		
		// parse out the command
		String operation = args[i].toUpperCase();
		OperationType op = null;
		try {
			op = OperationType.valueOf(operation);
		} catch (IllegalArgumentException e) {
			printUsage();
		}
		i++;
		
		// if the op requires files, exit if no files exist
		if (op != OperationType.LIST && i == args.length) {
			printUsage();
		}
		
		// parse out the filename arguments
		List<String> files = new ArrayList<String>();
		for (int j = i; j < args.length; j++) {
			files.add(args[j]);
		}
		
		// create instances used in operations
		RMIMessageReceiver receiver = null;
		Log l = new Logger();
		RMIMessage rmiServer = null;
		
		// execute the operation
		switch (op) {
		case PUT:
			System.out.println("execute put on the following files: " + files.toString());
			try {
				receiver = new RMIMessageReceiver();
				rmiServer = receiver.getRMIServer();
				if (rmiServer == null) {
					System.err.println("No RMI Server found");
					System.exit(1);
				}
			} catch (RemoteException e) {
				e.printStackTrace();
			}
			
			for (String f: files) {
				// check if the file exists, if so, delete or exit
				if (l.exists(f)) {
					if (promptOverwrite(f)) {
						Delete d = new Delete(f, rmiServer);
						d.send();
					} else {
						continue;
					}
				}
				
				// try a put on the file and display the result
				Put p = new Put(f, rmiServer, chunkSize, encrypt, compress, hash, publicfile);
				List<Message> result = p.send();
				if (result != null) {
					System.out.println("Put of " + f + " completed successfully");
				} else {
					System.out.println("Put of " + f + " failed");
				}
			}
			break;
		case GET:
			System.out.println("execute get on the following files: " + files.toString());
			// do it all in the 'for' loop, so we get a separate RMIMessage instance for each GET
			for (String f: files) {
				RMIMessage rmiServ = null;
				try {
					// set up RMI
					RMIMessageReceiver r = new RMIMessageReceiver();
					rmiServ = r.getRMIServer();
					if (rmiServ == null) {
						System.err.println("No RMI Server found");
						System.exit(1);
					}
					r.setupRMI(rmiServ);
				} catch (RemoteException e) {
					System.err.println("Unable to connect to RMI Server");
					System.exit(1);
				}
				long[] ids = null;
				List<Message> m = null;
				// get the ids to request for the current file
				try {
					ids = l.getIds(f);
				} catch (Exception e) {
					System.err.println("Get error: Unable to find log file for " + f);
				}
				if (ids != null) {
					Get g = new Get(ids, rmiServ);
					m = g.send();
				}
				if (ids == null || m == null) {
					System.exit(1);
				}
			}
			break;
		case LIST:
			System.out.println("execute list");
			List<String> filelist = l.list();
			if (filelist.isEmpty()) {
				System.out.println("No files in breadbox!");
			} else {
				System.out.println("Files in breadbox:");
			}
			for (String f : filelist) {
				System.out.println("\t"+f.substring(0, f.length() - Log.LOG_FILE_EXTENSION.length()));
			}
			break;
		case DELETE:
			System.out.println("execute delete on: " + files.toString());
			confirmDelete();
			receiver = new RMIMessageReceiver();
			rmiServer = receiver.getRMIServer();
			for (String file : files) {
				Delete d = new Delete(file, rmiServer);
				d.send();
			}
			break;
		default:
			printUsage();
			break;
		}
		if (op != OperationType.GET) {
			System.exit(0);
		}
	}

	/**
	 * Request a delete operation on a duplicate put
	 */
	private static boolean promptOverwrite(String f) {
		System.out.println("A file with name " + f + " already exists." +
				"Do you want to remove the old version first?");
		Scanner s = new Scanner(System.in);
		String answer = s.nextLine();
		return (answer.toUpperCase().startsWith("Y"));
	}
	
	/**
	 * Confirm a delete operation
	 */
	private static void confirmDelete() {
		System.out.println("Are you sure you want to delete these files?" +
				"(this action cannot be undone) Y or N");
		Scanner s = new Scanner(System.in);
		String answer = s.nextLine();
		boolean confirm = answer.toUpperCase().startsWith("Y");
		if (!confirm) {
			System.out.println("Delete aborted");
			System.exit(0);
		}
	}
	
	/**
	 * Print usage instructions and exit
	 */
	private static void printUsage() {
		System.out.println("USAGE NOTES:\n" +
				"[OPTION] OPERATION [File1] [File2] ...\n\n" +
				"OPTIONS:\n" +
				"\tChunksize: -chunksize n\n" +
				"\tHash check: -h\n" +
				"\tCompression: -c\n" +
				"\tEncryption: -e\n" +
				"OPERATIONS:\n" +
				"\tput: add file(s)\n" +
				"\tget: get file(s)\n" +
				"\tlist: get a list of available files\n" +
				"\tremove: remove file(s) from the service\n" +
				"FILES:\n" +
				"\tfilename1 [filename2 ...]: file(s) to perform operation on");
		System.exit(1);
	}
}
